package com.fary.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

public class GroupChatServer {

    /**
     * 定义属性
     */
    private Selector selector;

    /**
     * 定义ServerSocketChannel
     */
    private ServerSocketChannel serverSocketChannel;

    /**
     * 定义端口
     */
    private static final int SERVER_PORT = 6667;

    /**
     * 重写构造方法
     */
    public GroupChatServer () {
        try {
            // 1. 得到选择器
            selector = Selector.open();

            // 2. 初始化服务器Channel
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.socket().bind(new InetSocketAddress(SERVER_PORT));

            // 3. 设置服务器端的阻塞模式
            serverSocketChannel.configureBlocking(false);

            // 4. 将Channel注册到Selector中，并且绑定监听事件
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 写主核心方法
     *
     * @param args
     */
    public static void main(String[] args) {
        GroupChatServer groupChatServer = new GroupChatServer();
        groupChatServer.listen();
    }

    /**
     * 编写服务器的监听方法
     */
    public void listen () {
        try {
            // 1. 通过轮询的方式监听
            while (true) {
                // 2. 通过select()获取事件个数
                int count = selector.select();
                if (count ==  0) {
                    System.out.println("No client connect……please wait.");
                    continue;
                }
                if (count > 0 ) {
                    // 3. 获取所有事件对应的key
                    Set<SelectionKey> selectionKeys = selector.selectedKeys();

                    // 4. 开始遍历所有的key
                     Iterator<SelectionKey> iteratorKey = selectionKeys.iterator();
                    while (iteratorKey.hasNext()) {
                        // 5. 遍历取出对应的key
                        SelectionKey selectionKey = iteratorKey.next();
                        // 6. 监听是否是连接事件
                        if (selectionKey.isAcceptable()) {
                            // 7. 获取客户端的socket
                            SocketChannel socketChannel = serverSocketChannel.accept();
                            // 8. 将该socket设置为非阻塞的
                            socketChannel.configureBlocking(false);
                            // 9. 注册所有的读取事件
                            socketChannel.register(selector, SelectionKey.OP_READ);
                            System.out.println(socketChannel.getRemoteAddress() + " is connecting……");
                        }

                        // 10. 如果监听到有读取事件，则开始读取数据
                        if (selectionKey.isReadable()) {
                            // 11.  专门读取客户端的数据
                            readDateFromClient(selectionKey);
                        }
                        iteratorKey.remove();
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * 读取客户端数据
     *
     * @param selectionKey
     */
    private void readDateFromClient(SelectionKey selectionKey) {
        SocketChannel channel = null;
        try {
            // 1. 通过key获取到Channel
            channel = (SocketChannel) selectionKey.channel();

            // 2. 分配缓冲区接纳数据
            ByteBuffer buffer = ByteBuffer.allocate(1024);

            // 3. 开始读取数据，并且判断是否读取到数据
            int readCount = channel.read(buffer);
            if (readCount > 0) {
                // 4. 输出消息
                System.out.println("Read message from client: " + new String(buffer.array()));

                // 5. 同时将消息发送给其他客户端
                sendToOtherClient(buffer, channel);
            }
        } catch (IOException e) {
            // 如果系统抛出了异常，则需要专门处理
            try {
                System.out.println(channel.getRemoteAddress() + "is offlined");
                // 取消通道，并且关闭注册
                selectionKey.cancel();
                channel.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }

    /**
     * 将消息从一个客户端发送给你另一个客户端
     *
     * @param buffer                消息buffer
     * @param fromChannel           客户端Channel
     */
    private void sendToOtherClient(ByteBuffer buffer, SocketChannel fromChannel) {
        System.out.println("Send message to other client……");
        // 1. 遍历注册到Selector上面的所有的Channel，当时不要发送给自己了
        Iterator<SelectionKey> keys = selector.keys().iterator();
        while (keys.hasNext()) {
            SelectionKey key = keys.next();
            // 2. 同样通过key获取到对应的Channel
            Channel channel = key.channel();
            // 3. channel排除来源于自己 && channel != fromChannel
            if (channel instanceof SocketChannel ) {
                // 转化为对应的SocketChannel
                SocketChannel destChannel = (SocketChannel) channel;
                // 存储到buffer中，并且写出
                try {
                    destChannel.write(buffer);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}
